package testApplets;

import java.awt.geom.Line2D;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Vector;

import controlP5.ControlP5;
import controlP5.RadioButton;

import marchingCubes.IsMCVertex;
import marchingCubes.MCube;
import marchingCubes.MCubeEngine;
import marchingCubes.MCubeEngineLowMemory;
import meshToSolid.ContainerGrid;
import meshToSolid.STLWriter;
import meshToSolid.ContainerGrid.Container;

import peasy.PeasyCam;
import processing.core.PApplet;
import processing.core.PGraphics3D;
import processing.core.PMatrix3D;
import processing.core.PVector;

import simpleGeom.Bounds3D;
import simpleGeom.Box3D;
import simpleGeom.Face3D;
import simpleGeom.HasBounds3D;
import simpleGeom.HasPos3D;
import simpleGeom.Line3D;
import simpleGeom.SimpleTriangle;

import simpleGeom.Node;
import simpleGeom.SimpleObjLoaderStream;
import simpleGeom.Triangle3DUVN;
import simpleGeom.TrianglePreCalc;
import simpleGeom.Vec3D;

public class MeshToSolidCuts extends PApplet {
	ArrayList<Node> nodes = new ArrayList<Node>();
	ArrayList<SimpleTriangle> faces = new ArrayList<SimpleTriangle>();
	Bounds3D b3d = new Bounds3D();
	Bounds3D realBounds = new Bounds3D();
	Cell[][][] grid;
	ContainerGrid cGrid;
	int cGridRes = 20;
	ArrayList<Line3D> lines;
	float sX;
	float sY;
	float sZ;
	int nX;
	int nY;
	int nZ;
	int nYZ;
	float dimHigh = 0.015f;// 0.0034//0.003//0.007
	float dim = 0.03f;
	float dimLow = 0.012f;//0.1
	float hdim = dim * 0.5f;// 0.003//0.007
	float iso = dim * 0.90f;// 0.005//0.006

	String folderPath = "/Users/caad/Desktop/Archive/";
	
	String pathObjIn = folderPath + "skinTestVersion1gRight.obj";// form |
																	// 11-09-12
																	// |
																	// 14'28'40
																	// |.obj

	String pathDistOut = folderPath + "testDistHigh4.txt";
	Vector<SimpleTriangle> meFaces;
	boolean flagMarching = false;
	boolean flagDistance = false;

	ControlP5 cp5;
	
	public int minX = 26;// 26
	public int maxX = 5;// 5
	public int minY = 20;// 20
	public int maxY = 75;// 72
	public int minZ = 28;// 28

	String pathObjOut = "";
	Box3D innerVolume;
	Bounds3D meshBounds;
	PeasyCam cam;
	boolean isPreview = true;
	boolean flagResetPreview;
	boolean flagResetFine;
	boolean flagRefresh;
	float rotY = 0;
	Vector<SimpleTriangle> borderFaces = new Vector<SimpleTriangle>();
	float[][][] bkpDist;
	public void setup() {
		size(1300, 750, P3D);
		//
		b3d = new Bounds3D();
		realBounds = new Bounds3D();
		realBounds.addPoint(new Vec3D(1.5f, 1.5f, 1.5f));
		realBounds.addPoint(new Vec3D(0, 0, 0));
		loadData();

		resetFine();
		findBorders();
		createGUI();
		
	}

	public void radioButton(int a) {
		flagRefresh = true;
		rotY = HALF_PI * a*0.5f;
		// println("a radio Button event: "+a);
	}

	public void resetPreview() {
		dim = dimLow;
		iso = dim * 0.90f;
		 hdim = dim * 0.5f;
		setBounds();
		createGrid();
		calculateDistancesSimple();
		
		findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		println("start marchincube");
		meFaces = MCubeEngineLowMemory.initCubesTest(grid, iso, null);
		findBorders();
	}

	public void resetFine() {
		meFaces = null;
		dim = dimHigh;
		iso = dim * 0.90f;
		 hdim = dim * 0.5f;
		setBounds();
		createGrid();
		
		calculateDistances();
	
		findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		for (int x = 0; x < nX; x++) {
			println("x: " + x + "/" + nX);
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell=grid[x][y][z];
					bkpDist[x][y][z]=cell.getValue();
				}
			}
		}
		Box3D b=new Box3D();
		float dX=b3d.getDimX();
		float dY=b3d.getDimY()*0.5f;
		float dZ=b3d.getDimZ()*0.33f;
		for (int y=0;y<2;y++){
			for (int z=0;z<3;z++){
				b.x1=b3d.x1;
				b.y1=b3d.y1+y*dY;
				b.z1=b3d.z1+z*dZ;
				b.x2=b3d.x2;
				b.y2=b3d.y1+(y+1)*dY;
				b.z2=b3d.z1+(z+1)*dZ;
				findOutlineBorder(b);
				String path=pathObjIn+"y"+y+"z"+z+".obj";
				save(path);
			}
		}
		
		
		
		
		

	}
	public void save(String path){
		println("start marchincube");
		PrintWriter output = createWriter(path);
		meFaces = MCubeEngineLowMemory.initCubesTest(grid, iso, output);
		meFaces = new Vector<SimpleTriangle>();
		println("start save");
		output.flush(); // Writes the remaining data to the file
		output.close();
		println("saved");
	}

	public void createGUI() {
		cp5 = new ControlP5(this);
		cp5.addSlider("minX").setPosition(20, 20).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("maxX").setPosition(20, 50).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("minY").setPosition(20, 80).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("maxY").setPosition(20, 110).setRange(0, 100)
				.setSize(200, 20);
		;
		cp5.addSlider("minZ").setPosition(20, 140).setRange(0, 100)
				.setSize(200, 20);
		;
		//cp5.setAutoDraw(false);

		RadioButton r = cp5.addRadioButton("radioButton").setPosition(20, 170)
				.setSize(10, 10).setColorForeground(color(120))
				.setColorActive(color(255)).setColorLabel(color(255))
				.setItemsPerRow(8).setSpacingColumn(20).addItem("50", 1)
				.addItem("100", 2)
				.addItem("150", 3)
				.addItem("200", 4)
				.addItem("250", 5)
				.addItem("300", 6)
				.addItem("350", 7)
				.addItem("400", 8)

		;
	}

	public void saveSTL(String file) {
		STLWriter writer = new STLWriter();
		writer.beginSave(file + ".stl", meFaces.size());
		for (int i = 0; i < meFaces.size(); i++) {
			SimpleTriangle f = meFaces.get(i);
			writer.face(f.getNode(0), f.getNode(1), f.getNode(2));
		}
		writer.endSave();
	}

	public void loadData() {
		SimpleObjLoaderStream stream = new SimpleObjLoaderStream();
		stream.init(pathObjIn);
		float[] vertex = stream.readVertex();
		while (vertex != null) {
			nodes.add(new Node(vertex[0], vertex[1], vertex[2]));
			vertex = stream.readVertex();
		}
		println("nodes: " + nodes.size());
		stream.close();
		stream.init(pathObjIn);
		int[] face = stream.readFace();
		while (face != null) {
			SimpleTriangle f = new SimpleTriangle(null, null, null);
			if (face.length == 4) {
				f = new SimpleTriangle(nodes.get(face[0] - 1),
						nodes.get(face[1] - 1), nodes.get(face[2] - 1));
				faces.add(f);
				f = new SimpleTriangle(nodes.get(face[2] - 1),
						nodes.get(face[3] - 1), nodes.get(face[0] - 1));
				faces.add(f);

				for (int i = 0; i < face.length; i++) {
					Node n = nodes.get(face[i] - 1);
					n.setComment(1);
				}
			} else {
				f = new SimpleTriangle(nodes.get(face[0] - 1),
						nodes.get(face[1] - 1), nodes.get(face[2] - 1));
				for (int i = 0; i < face.length; i++) {
					Node n = nodes.get(face[i] - 1);
					n.setComment(1);
				}
				faces.add(f);
			}
			face = stream.readFace();
		}
		ArrayList<Node> newNodes = new ArrayList<Node>();
		for (int i = 0; i < nodes.size(); i++) {
			Node n = nodes.get(i);
			if (n.getComment() == 1) {
				newNodes.add(n);
			}
		}
		nodes = newNodes;
		realBounds = new Bounds3D();
		realBounds.addAll(nodes);
		realBounds.z2 = -realBounds.z1;

		stream.close();
		stream = null;
	}

	public void findBorders() {
		borderFaces = new Vector<SimpleTriangle>();
		for (int i = 0; i < faces.size(); i++) {
			SimpleTriangle tri = faces.get(i);
			Box3D box = tri.getBounds3D();
			if (box.getX1() < b3d.getX1() && box.getX2() > b3d.getX1())
				borderFaces.add(tri);

			if (box.getX1() < b3d.getX2() && box.getX2() > b3d.getX2()) {
				borderFaces.add(tri);
			}
			if (box.getY1() < b3d.getY1() && box.getY2() > b3d.getY1()) {
				borderFaces.add(tri);
			}
			if (box.getY1() < b3d.getY2() && box.getY2() > b3d.getY2()) {
				borderFaces.add(tri);
			}
			if (box.getZ1() < b3d.getZ1() && box.getZ2() > b3d.getZ1()) {
				borderFaces.add(tri);
			}
			if (box.getZ1() < b3d.getZ2() && box.getZ2() > b3d.getZ2()) {
				borderFaces.add(tri);
			}
		}
	}

	public void mirrorNodes() {
		for (int i = 0; i < nodes.size(); i++) {
			Node n = nodes.get(i);
			n.z *= -1;
		}
	}

	public void marchinCube() {
		findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		println("start marchincube");
		PrintWriter output = createWriter(pathObjOut);

		meFaces = MCubeEngineLowMemory.initCubesTest(grid, iso, output);
		println("start save");

		output.flush(); // Writes the remaining data to the file
		output.close();
		println("saved");
		// this.saveObjPrintwriter(pathObjOut);
	}

	public void calculateDistancesSimple() {

		// Box3D box3D = b3d.getOffsetBox(dim*4f+iso);

		for (int i = 0; i < faces.size(); i++) {
			if (i % 1000000 == 0) {
				println(i + "/" + faces.size());
			}
			SimpleTriangle f = (SimpleTriangle) faces.get(i);

			Box3D faceBounds = f.getBounds3D();
			// faceBounds = faceBounds.getOffsetBox(iso);
			int x1 = xK(faceBounds.x1) - 1;
			int y1 = yK(faceBounds.y1) - 1;
			int z1 = zK(faceBounds.z1) - 1;
			x1 = max(x1, 0);
			y1 = max(y1, 0);
			z1 = max(z1, 0);
			int x2 = xK(faceBounds.x2) + 1;
			int y2 = yK(faceBounds.y2) + 1;
			int z2 = zK(faceBounds.z2) + 1;
			x2 = min(x2, nX);
			y2 = min(y2, nY);
			z2 = min(z2, nZ);

			HasPos3D v1 = f.getNode(0);
			HasPos3D v2 = f.getNode(1);
			HasPos3D v3 = f.getNode(2);
			Vec3D v12 = Vec3D.sub(v2, v1);
			Vec3D v13 = Vec3D.sub(v3, v1);
			Vec3D n = Vec3D.cross(v12, v13);
			n.normalize();
			float d0 = -n.dot(f.getNode(0));
			for (int x = x1; x < x2; x++) {
				for (int y = y1; y < y2; y++) {
					for (int z = z1; z < z2; z++) {
						float planeDist = n.dot(x * dim + sX, y * dim + sY, z
								* dim + sZ)
								+ d0;
						if (planeDist < iso) {
							Cell cell = grid[x][y][z];
							cell.setValue(0);
						}
					}
				}
			}
		}
	}

	public void calculateDistances() {

		// Box3D box3D = b3d.getOffsetBox(dim*4f+iso);

		for (int i = 0; i < faces.size(); i++) {
			if (i % 100000 == 0) {
				println(i + "/" + faces.size());
			}
			SimpleTriangle f = (SimpleTriangle) faces.get(i);

			Box3D faceBounds = f.getBounds3D();
			faceBounds = faceBounds.getOffsetBox(iso);
			int x1 = xK(faceBounds.x1) - 1;
			int y1 = yK(faceBounds.y1) - 1;
			int z1 = zK(faceBounds.z1) - 1;
			x1 = max(x1, 0);
			y1 = max(y1, 0);
			z1 = max(z1, 0);
			int x2 = xK(faceBounds.x2) + 1;
			int y2 = yK(faceBounds.y2) + 1;
			int z2 = zK(faceBounds.z2) + 1;
			x2 = min(x2, nX);
			y2 = min(y2, nY);
			z2 = min(z2, nZ);

			PMatrix3D m = new PMatrix3D();
			Vec3D planeAxis = new Vec3D(0, 0, 1);
			Line3D l1;
			Line3D l2;
			Line3D l3;

			HasPos3D v1 = f.getNode(0);
			HasPos3D v2 = f.getNode(1);
			HasPos3D v3 = f.getNode(2);
			Vec3D v12 = Vec3D.sub(v2, v1);
			Vec3D v13 = Vec3D.sub(v3, v1);
			Vec3D n = Vec3D.cross(v12, v13);
			n.normalize();
			float angle = (float) Math.acos(n.dot(planeAxis));

			Vec3D u = n.cross(planeAxis);
			u.normalize();

			float c = (float) Math.cos(angle);
			float s = (float) Math.sin(angle);
			float t = 1f - c;

			m.m00 = c + u.getX() * u.getX() * t;
			m.m01 = u.getX() * u.getY() * t - u.getZ() * s;
			m.m02 = u.getX() * u.getZ() * t + u.getY() * s;

			m.m10 = u.getY() * u.getX() * t + u.getZ() * s;
			m.m11 = c + u.getY() * u.getY() * t;
			m.m12 = u.getY() * u.getZ() * t - u.getX() * s;

			m.m20 = u.getZ() * u.getX() * t - u.getY() * s;
			m.m21 = u.getZ() * u.getY() * t + u.getX() * s;
			m.m22 = c + u.getZ() * u.getZ() * t;

			float[] f1 = new float[] { v1.getX(), v1.getY(), v1.getZ() };
			float[] f2 = new float[] { v2.getX(), v2.getY(), v2.getZ() };
			float[] f3 = new float[] { v3.getX(), v3.getY(), v3.getZ() };

			float[] f1T = m.mult(f1, null);
			float[] f2T = m.mult(f2, null);
			float[] f3T = m.mult(f3, null);

			l1 = new Line3D(f1T[0], f1T[1], f1T[2], f2T[0], f2T[1], f2T[2]);
			l2 = new Line3D(f2T[0], f2T[1], f2T[2], f3T[0], f3T[1], f3T[2]);
			l3 = new Line3D(f3T[0], f3T[1], f3T[2], f1T[0], f1T[1], f1T[2]);

			float d0 = -n.dot(f.getNode(0));

			for (int x = x1; x < x2; x++) {
				for (int y = y1; y < y2; y++) {
					for (int z = z1; z < z2; z++) {
						// float minD = Float.MAX_VALUE;
						Cell cell = grid[x][y][z];
						float cellX = cell.getX();
						float cellY = cell.getY();
						float cellZ = cell.getZ();
						if (faceBounds.isOverlap(cellX - dim, cellY - dim,
								cellZ - dim, cellX + dim, cellY + dim, cellZ
										+ dim)) {
							float planeDist = n.dot(cellX, cellY, cellZ) + d0;

							if (planeDist < cell.getValue()) {
								float cDist = Float.MAX_VALUE;
								float[] pt = new float[] { cellX, cellY, cellZ };
								float[] trans = new float[3];

								trans = m.mult(pt, null);
								float cX = trans[0];
								float cY = trans[1];
								float cZ = trans[2];

								int r1 = Line2D.relativeCCW(l1.x1, l1.y1,
										l1.x2, l1.y2, cX, cY);
								int r2 = Line2D.relativeCCW(l2.x1, l2.y1,
										l2.x2, l2.y2, cX, cY);
								int r3 = Line2D.relativeCCW(l3.x1, l3.y1,
										l3.x2, l3.y2, cX, cY);
								float cDistance;
								if (r1 == r2 && r1 == r3) {
									cDistance = (cZ - l1.z1) * (cZ - l1.z1);
									if (cDistance < cDist) {
										cDist = cDistance;
									}
								} else {
									float d1 = (float) l1.ptSegDistSq(cX, cY,
											cZ);
									float d2 = (float) l2.ptSegDistSq(cX, cY,
											cZ);
									float d3 = (float) l3.ptSegDistSq(cX, cY,
											cZ);
									if (d1 < cDist) {
										cDist = d1;
									}
									if (d2 < cDist) {
										cDist = d2;
									}
									if (d3 < cDist) {
										cDist = d3;
									}
								}
								cDist = sqrt(cDist);
								if (cDist < cell.getValue()) {
									cell.setValue(cDist);
								}
							}
						}
					}
				}
			}
		}
		
	}

	public void createGrid() {
		nX = (int) (b3d.getDimX() / dim);
		nY = (int) (b3d.getDimY() / dim);
		nZ = (int) (b3d.getDimZ() / dim);
		nYZ = nY * nZ;

		sX = b3d.getX1();
		sY = b3d.getY1();
		sZ = b3d.getZ1();
		grid = new Cell[nX][nY][nZ];
		bkpDist=new float[nX][nY][nZ];
		println(nX + " " + nY + " " + nZ);
		int id = 0;
		for (int x = 0; x < nX; x++) {
			println("x: " + x + "/" + nX);
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					float x1 = x * dim + sX;
					float y1 = y * dim + sY;
					float z1 = z * dim + sZ;
					Cell cell = new Cell(x1, y1, z1);
					cell.id = id;
					id++;
					cell.setValue(1000);
					grid[x][y][z] = cell;
				}
			}
		}
	}

	public void loadDistance(String file) {
		String[] lines = this.loadStrings(file);
		String cString = lines[0];
		String[] strings = cString.split(" ");
		int nX = Integer.valueOf(strings[0]);
		println("nX: " + nX);
		int nY = Integer.valueOf(strings[1]);
		int nZ = Integer.valueOf(strings[2]);

		grid = new Cell[nX][nY][nZ];
		println(nY);
		int j = 0;
		for (int i = 1; i < lines.length; i++) {
			String l = lines[i];
			j = i - 1;
			int cX = ((int) (j * 1f / (nZ * nY)));
			int cY = (int) (j * 1f / nZ) % nY;
			int cZ = j % (nZ);
			double d = Double.valueOf(l);
			grid[cX][cY][cZ].d = (float) d;
		}
	}

	public void saveDistance(String file) {
		try {
			FileOutputStream fileOutputStream = new FileOutputStream(file);
			PrintStream printstream = new PrintStream(fileOutputStream);
			for (int x = 0; x < grid.length; x++) {
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						Cell c = grid[x][y][z];
						printstream.println("" + c.d);
					}
				}
			}
			printstream.close();
			fileOutputStream.close();
		} catch (Exception e) {
			System.err.println("error writing file");
		}
	}

	public void saveObjPrintwriter(String file) {
		PrintWriter output = createWriter(file + ".obj");

		int idV = 1;
		meshBounds = new Bounds3D();
		for (int i = 0; i < meFaces.size(); i++) {
			SimpleTriangle f = meFaces.get(i);
			for (int j = 0; j < f.getNodesNum(); j++) {
				Vec3D n = (Vec3D) f.getNode(j);
				meshBounds.addPoint(n);
				if (n.getId() == 0) {
					n.setId(idV);
					idV++;
					output.println("v " + n.x + " " + n.y + " " + n.z);
				}
			}
		}

		output.println("g inside");
		innerVolume = meshBounds.getOffsetBox(-0.01f);
		for (int i = 0; i < meFaces.size(); i++) {
			SimpleTriangle f = meFaces.get(i);
			int[] ids = new int[f.getNodesNum()];
			String string = "f";
			boolean isInside = false;
			for (int j = 0; j < f.getNodesNum(); j++) {
				Vec3D n = (Vec3D) f.getNode(j);
				if (innerVolume.contains(n.x, n.y, n.z)) {
					isInside = true;
				}
				string += " " + n.getId();
			}
			if (isInside) {
				output.println(string);
			}
		}
		output.println("g hull");
		println(meshBounds);
		println(innerVolume);
		for (int i = 0; i < meFaces.size(); i++) {
			SimpleTriangle f = meFaces.get(i);
			int[] ids = new int[f.getNodesNum()];
			String string = "f";
			boolean isInside = false;
			int nOutside = 0;
			for (int j = 0; j < f.getNodesNum(); j++) {
				Vec3D n = (Vec3D) f.getNode(j);
				if (innerVolume.contains(n.x, n.y, n.z)) {
					isInside = true;
				}
				string += " " + n.getId();
			}

			if (!isInside) {
				output.println(string);
			}
		}
		output.flush(); // Writes the remaining data to the file
		output.close();
	}

	public void findOutline() {

		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				boolean flag = false;
				for (int x = nX - 1; x >= 0; x--) {
					Cell nb = grid[x][y][z];

					if (nb.getValue() > iso) {
						nb.setValue(0);
					} else {
						flag = true;
						x = -1;
					}
				}
				if (!flag) {
					for (int x = nX - 1; x >= 0; x--) {
						Cell nb = grid[x][y][z];
						nb.setValue(1000);
					}
				}
			}
		}
		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				Cell nb = grid[nX - 1][y][z];
				nb.setValue(1000);
			}
		}
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					if (y < 20 || y > nY - 15) {
						Cell nb = grid[x][y][z];
						nb.setValue(1000);
					}
				}
			}
		}
	}

	public void findOutlineBorder(Box3D b) {
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!b.contains(cell.getX(), cell.getY(), cell.getZ()))
						cell.setValue(1000);
					else{
						cell.setValue(bkpDist[x][y][z]);
					}
				}
			}
		}
	}

	public void findOutline(int cX, int cY, int cZ) {
		Vector<Cell> candidates = new Vector<Cell>();

		/*
		 * for (int x=0;x<nX;x++){ for (int y=0;y<nY;y++){
		 * candidates.add(grid[x][y][0]); candidates.add(grid[x][y][nZ-1]); } }
		 * for (int x=0;x<nX;x++){ for (int z=0;z<nZ;z++){
		 * candidates.add(grid[x][0][z]); candidates.add(grid[x][nY-1][z]); } }
		 * for (int y=0;y<nY;y++){ for (int z=0;z<nZ;z++){
		 * candidates.add(grid[0][y][z]); candidates.add(grid[nX-1][y][z]); } }
		 */
		candidates.add(grid[cX][cY][cZ]);
		for (int i = 0; i < candidates.size(); i++) {
			Cell cell = candidates.get(i);
			cell.inside = true;
		}

		while (candidates.size() > 0) {
			Vector<Cell> nextCandidates = new Vector<Cell>();
			for (int i = 0; i < candidates.size(); i++) {
				Cell cell = candidates.get(i);
				/*
				 * int sX = max(xK(cell.getX()) - 1, 0); int sY =
				 * max(yK(cell.getY()) - 1, 0); int sZ = max(zK(cell.getZ()) -
				 * 1, 0);
				 * 
				 * int eX = min(xK(cell.getX()) + 2, nX); int eY =
				 * min(yK(cell.getY()) + 2, nY); int eZ = min(zK(cell.getZ()) +
				 * 2, nZ);
				 */

				int sX = max(cell.getXK() - 1, 0);
				int sY = max(cell.getYK() - 1, 0);
				int sZ = max(cell.getZK() - 1, 0);

				int eX = min(cell.getXK() + 2, nX);
				int eY = min(cell.getYK() + 2, nY);
				int eZ = min(cell.getZK() + 2, nZ);

				for (int x = sX; x < eX; x++) {
					for (int y = sY; y < eY; y++) {
						for (int z = sZ; z < eZ; z++) {
							Cell nb = grid[x][y][z];
							if (!nb.inside && nb.getValue() > iso) {
								nb.inside = true;
								nextCandidates.add(nb);
							}
						}
					}
				}
			}
			candidates = nextCandidates;
		}
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.inside && cell.getValue() > iso) {
						cell.setValue(0);//0
					}
				}
			}
		}
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.inside) {
						if (x == 0 || x == nX - 1 || y == 0 || y == nY - 1
								|| z == 0 || z == nZ - 1) {
							cell.setValue(1000);
						}
					}

				}
			}
		}

		// now find inner border= cell with lower iso
		// start to grow inside, cells with higher iso but inside
		// set the value iso + celldim
		// all cells inside with iso higher a certain range are outside again.

	}

	public int xK(float x) {
		return Math.round((x - sX) / dim);
	}

	public int yK(float y) {
		return Math.round((y - sY) / dim);
	}

	public int zK(float z) {
		return Math.round((z - sZ) / dim);
	}

	void gui() {
		//noLights();
		stroke(0);
		fill(0);

		
		
		//PGraphics3D p3d = ((PGraphics3D) g);
		//PMatrix3D currCameraMatrix = new PMatrix3D(p3d.camera);

		camera();
		//rect(0, 0, 200, 800);
		//cp5.draw();
		//p3d.camera = currCameraMatrix;
		

	}

	public void setBounds() {
		b3d.x1 = realBounds.x1 + minX / 100f;
		b3d.x2 = realBounds.x2 + maxX / 100f; // 0.05F
		b3d.z1 = realBounds.z1 + minZ / 100f;// 0.5
		b3d.z2 = realBounds.z2 - minZ / 100f;// 0.5f
		b3d.y1 = realBounds.y1 + minY / 100f;// 0.45
		b3d.y2 = realBounds.y2 - maxY / 100f;
	}

	public void draw() {
		setBounds();
		if (flagResetPreview) {
			resetPreview();
			flagResetPreview = false;
		}
		if (flagResetFine) {
			resetFine();
			flagResetFine = false;
		}
		if (flagDistance) {
			createGrid();
			calculateDistances();
			mirrorNodes();
			calculateDistances();
			flagDistance = false;
		}

		if (flagMarching) {
			marchinCube();
			flagMarching = false;
		}
		
		
		
		if (flagRefresh) {
			println("draw");
			randomSeed(1);
		
			camera(800,0,0,0,0,0,0,-1,0);
			lights();
			hint(ENABLE_DEPTH_TEST);
			pushMatrix();
			rectMode(CENTER);
			background(0);
			noStroke();
			fill(255);
			scale(200, 200, 200);
			// translate(-b3d.getCenterX(), -b3d.getCenterY(),
			// -b3d.getCenterZ());
			/*pointLight(200, 200, 200, b3d.getDimX(), b3d.getDimY(),
					b3d.getDimZ());
			pointLight(200, 200, 200, -b3d.getDimX(), -b3d.getDimY(),
					-b3d.getDimZ());*/
			rotateY(rotY);
		//	PGraphicsHelper.drawWorldAxis3D(g, 0.5f);
			float tol = 0.03f;
			
			fill(255);
			if (meFaces != null) {
				for (int i = 0; i < meFaces.size(); i++) {
					SimpleTriangle f = meFaces.get(i);
					f.display3D(g);
				}
			}
			noFill();
			stroke(0, 0, 255);
			realBounds.display3D(g);
			stroke(255, 0, 0);
			noStroke();
			fill(255,30);
			b3d.display3D(g);
			
			fill(255,0,0);
			stroke(255,0,0);
			strokeWeight(2f);
			for (int i = 0; i < borderFaces.size(); i++) {
				SimpleTriangle tri = borderFaces.get(i);
				tri.display3D(g);
			}
			flagRefresh = false;
			popMatrix();
			hint(DISABLE_DEPTH_TEST);
		}
		

		gui();
	}

	public class Cell implements IsMCVertex {
		// private float x;
		// private float y;
		// private float z;
		int id;
		float d = Float.MAX_VALUE;
		boolean inside;
		MCube cube;

		public Cell() {
			super();
			// TODO Auto-generated constructor stub
		}

		public Cell(float x, float y, float z) {
			/*
			 * this.x=x; this.y=y; this.z=z;
			 */
		}

		public Cell(int id) {
			this.id = id;
		}

		public float getValue() {
			// TODO Auto-generated method stub
			return d;
		}

		public void setValue(double value) {
			// TODO Auto-generated method stub
			this.d = (float) value;
		}

		public MCube getCube() {
			// TODO Auto-generated method stub
			return cube;
		}

		@Override
		public float getX() {
			// TODO Auto-generated method stub
			// return nX%3;
			return (int) (id * 1f / (nYZ)) * dim + sX;
			// return x;
		}

		@Override
		public float getY() {
			// TODO Auto-generated method stub
			return ((int) (id / nZ)) % nY * dim + sY;
			// return y;
		}

		public float getZ() {
			// TODO Auto-generated method stub
			return id % nZ * dim + sZ;
			// return z;
		}

		public int getXK() {
			// TODO Auto-generated method stub
			// return nX%3;
			return (int) (id * 1f / (nYZ));
			// return x;
		}

		public int getYK() {
			// TODO Auto-generated method stub
			return ((int) (id / nZ)) % nY;
			// return y;
		}

		public int getZK() {
			// TODO Auto-generated method stub
			return id % nZ;
			// return z;
		}

		public void setCube(MCube cube) {
			// TODO Auto-generated method stub
			this.cube = cube;
		}

		@Override
		public void setPosition(float x, float y, float z) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setZ(float z) {
			// TODO Auto-generated method stub
			// this.z=z;
		}

		@Override
		public void setPosition(float x, float y) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setX(float x) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setY(float Y) {
			// TODO Auto-generated method stub

		}

		@Override
		public int getId() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public void setId(int i) {
			// TODO Auto-generated method stub

		}
	}

	public void keyPressed() {
		if (key == 'd') {
			flagDistance = true;
			flagMarching = true;
		}
		if (key == 'f') {
			// flagMarching = true;
		}
		if (key == 'r') {
			flagResetPreview = true;
		}
		if (key == 't') {
			flagResetFine = true;
		}
		flagRefresh=true;

	}
}
